package com.highper.gateway.balancer;

import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.client.naming.utils.RandomUtils;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.highper.common.constant.PortConstant.HIGHPER_APPLICATION_GRAYSCALE;
import static com.highper.common.constant.PortConstant.HIGHPER_APPLICATION_NORMAL;

/**
 * @Author ZhangGJ
 * @Date 2022/02/16 10:54
 */
@Slf4j
public class GrayLoadBalancerRule extends AbstractLoadBalancerRule {

    private int pollingIndex = 0;

    private static final String GRAYSCALE = "grayscale";

    private static final String NORMAL = "normal";

    private static final String TRUE = "true";

    //    private final DiscoveryClient discoveryClient;
    //
    //    public GrayRoundBalanceRule(DiscoveryClient discoveryClient) {
    //        this.discoveryClient = discoveryClient;
    //    }

    @Override
    public Server choose(Object key) {
        List<Server> loadBalancerServers = this.getLoadBalancer().getReachableServers();
        if (loadBalancerServers == null || loadBalancerServers.isEmpty()) {
            return null;
        }
        if (loadBalancerServers.size() == 1) {
            return loadBalancerServers.get(0);
        }
        if (key == null || StringUtils.isEmpty(key)) {
            return polling(loadBalancerServers);
        }
        Map<Object, List<Server>> collect =
                loadBalancerServers.stream().collect(Collectors.groupingBy(map -> {
                    NacosServer nacosServer = (NacosServer) map;
                    Map<String, String> metadata = nacosServer.getMetadata();
                    String grayscale = metadata.get(GRAYSCALE);
                    if (StringUtils.isEmpty(grayscale)) {
                        grayscale = NORMAL;
                    } else {
                        if (TRUE.equals(grayscale)) {
                            grayscale = GRAYSCALE;
                        } else {
                            grayscale = NORMAL;
                        }
                    }
                    return grayscale;
                }));
        if (collect == null || collect.size() == 0) {
            return null;
        }
        List<Server> gray = collect.get(GRAYSCALE);
        List<Server> normal = collect.get(NORMAL);
        if (gray != null && gray.size() != 0) {
            return polling(gray);
        } else {
            if (normal == null || normal.size() == 0) {
                return null;
            } else {
                return polling(normal);
            }
        }
    }

    private Server randomChoose(List<Server> servers) {
        int randomIndex = RandomUtils.nextInt(servers.size());
        return servers.get(randomIndex);
    }

    private Server hashKeyChoose(List<Server> servers, Object key) {
        int hashCode = Math.abs(key.hashCode());
        if (hashCode < servers.size()) {
            return servers.get(hashCode);
        }
        int index = hashCode % servers.size();
        return servers.get(index);
    }

    private Server polling(List<Server> servers) {
        try {
            int index = pollingIndex++ % servers.size();
            return servers.get(index);
        } catch (Exception e) {
            log.error("Grayscale polling error, switch random");
            return randomChoose(servers);
        }
    }

    @Override
    public void initWithNiwsConfig(IClientConfig config) {

    }

}
